#include "adc.h"

static unsigned long get_pclk(unsigned long cclk, unsigned long sel)
{
  if (sel == 0)
    return cclk >> 2;
  if (sel == 1)
    return cclk;
  if (sel == 2)
    return cclk >> 1;
  return cclk >> 3;
}

unsigned long get_adc0_clk(unsigned long fosc)
{
  unsigned long cclk = liblpc2000_lpc23xx_get_cclk(fosc);
  unsigned long pclk = get_pclk(cclk, (PCLKSEL0 & PCLKSEL0_PCLK_ADC_MASK) >> PCLKSEL0_PCLK_ADC_BIT);
  return pclk;
}

void adc_enable()
{
    unsigned long pclk = get_adc0_clk(OSCILLATOR_CLOCK_FREQUENCY);
    PCONP |= (1 << 12);

    /* all the related pins are set to ADC inputs, AD0.0~7 */
    PINSEL1 &= ~0x003FC000; /* P0.23~26, A0.0~3, function 01 */
    PINSEL1 |=  0x00154000;

    AD0CR = ( 0x01 << AD0CR_SEL_BIT ) |         /* SEL=1,select channel 0~7 on ADC0 */
            ( ( pclk / 1000000 - 1 ) << 8 ) |   /* CLKDIV = Fpclk / 1000000 - 1 (1MHz)*/ 
            ( 0 << AD0CR_BURST_BIT ) |          /* BURST = 0, no BURST, software controlled */
            ( 0 << AD0CR_CLKS_BIT ) |           /* CLKS = 0, 11 clocks/10 bits */
            ( 1 << AD0CR_PDN_BIT ) |            /* PDN = 1, normal operation */
            ( 0 << 22 ) |                       /* TEST1:0 = 00 */
            ( 0 << 24 ) |                       /* START = 0 A/D conversion stops */
            ( 0 << 27 );                        /* EDGE = 0 (CAP/MAT singal falling,trigger A/D conversion) */ 

    return;

}

unsigned int adc_read(int channel)
{
    unsigned int value;
    if ( channel >= 7 ) { channel = 0; }

    AD0CR &= 0xFFFFFF00;
    AD0CR |= (1 << AD0CR_START_BIT) | (1 << channel);   // switch channel,start A/D convert

    while (1)
    {
      value = AD0GDR;
      if (value & AD0GDR_DONE_MASK) break;
    }

    AD0CR &= ~(1<<AD0CR_START_BIT);//0xF8FFFFFF;            // stop ADC now     
    value >>= 6; 
    value &= 0x3FF;
    return value;            // return A/D conversion value
}

void adc_disable()
{
  // Stop conversion
  AD0CR &= ~(1<<AD0CR_START_BIT);
  // Disable ADC
  AD0CR &= ~(1<<AD0CR_PDN_BIT);
  // Power off
  PCONP &= ~(1<<PCONP_PCAD_BIT);
}